/*
 *    Copyright (c) 2018-2025, lengleng All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice,
 * this list of conditions and the following disclaimer.
 * Redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution.
 * Neither the name of the pig4cloud.com developer nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 * Author: lengleng (wangiegie@gmail.com)
 */
package com.pig4cloud.pigx.strategy.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pig4cloud.pigx.common.customize.enums.CommonDictEnum;
import com.pig4cloud.pigx.common.customize.exception.DuckweedException;
import com.pig4cloud.pigx.common.customize.redis.utils.RedisUtils;
import com.pig4cloud.pigx.common.customize.security.utils.SecurityUtil;
import com.pig4cloud.pigx.common.customize.utils.CommonUtil;
import com.pig4cloud.pigx.common.security.service.PigxUser;
import com.pig4cloud.pigx.strategy.constant.StrategyRedisLockConstant;
import com.pig4cloud.pigx.strategy.dto.StrategyStopOrderContractAddBatchDTO;
import com.pig4cloud.pigx.strategy.dto.StrategyStopOrderContractAddDTO;
import com.pig4cloud.pigx.strategy.dto.StrategyStopOrderContractCancelBatchDTO;
import com.pig4cloud.pigx.strategy.dto.StrategyStopOrderContractConditionDTO;
import com.pig4cloud.pigx.strategy.entity.StrategyStopOrderContract;
import com.pig4cloud.pigx.strategy.enums.StrategyOrderContractEnum;
import com.pig4cloud.pigx.strategy.enums.StrategyStopOrderContractEnum;
import com.pig4cloud.pigx.strategy.enums.StrategyTaskContractEnum;
import com.pig4cloud.pigx.strategy.executor.contract.stop_order.StrategyStopOrderContractHandlerManager;
import com.pig4cloud.pigx.strategy.executor.contract.task.StrategyTaskContractHandler;
import com.pig4cloud.pigx.strategy.mapper.StrategyStopOrderContractMapper;
import com.pig4cloud.pigx.strategy.mapper.StrategyTaskContractMapper;
import com.pig4cloud.pigx.strategy.service.StrategyStopOrderContractService;
import com.pig4cloud.pigx.strategy.util.StrategyContractUtil;
import com.pig4cloud.pigx.system.entity.SystemLog;
import com.pig4cloud.pigx.system.enums.SystemLogEnum;
import lombok.AllArgsConstructor;
import org.apache.logging.log4j.util.Strings;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

/**
 * 策略止盈止损-合约
 *
 * @since 2022-11-24 10:39:26
 */
@Service
@AllArgsConstructor
public class StrategyStopOrderContractServiceImpl extends ServiceImpl<StrategyStopOrderContractMapper, StrategyStopOrderContract> implements StrategyStopOrderContractService {

    private final StrategyStopOrderContractHandlerManager strategyStopOrderContractHandlerManager;
    private final StrategyTaskContractHandler strategyTaskContractHandler;
    private final RedisUtils redisUtils;
    private final SecurityUtil securityUtil;
    private final CommonUtil commonUtil;
    private final StrategyContractUtil strategyContractUtil;
    private final StrategyTaskContractMapper strategyTaskContractMapper;

    /**
     * 分页查询
     *
     * @param page      分页条件
     * @param condition 查询条件
     * @return {@link Page < StrategyStopOrderContract>}
     * @since 2023/2/13 09:49
     */
    @Override
    public Page<StrategyStopOrderContract> page(Page<StrategyStopOrderContract> page, StrategyStopOrderContractConditionDTO condition) {
        QueryWrapper<StrategyStopOrderContract> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda()
                .eq(!Objects.isNull(condition.getPlatformId()), StrategyStopOrderContract::getPlatformId, condition.getPlatformId())
                .eq(!Objects.isNull(condition.getTaskId()), StrategyStopOrderContract::getTaskId, condition.getTaskId())
                .eq(!Objects.isNull(condition.getUserId()), StrategyStopOrderContract::getUserId, condition.getUserId())
                .eq(!Objects.isNull(condition.getPositionId()), StrategyStopOrderContract::getPositionId, condition.getPositionId())
                .eq(Strings.isNotBlank(condition.getSide()), StrategyStopOrderContract::getSide, condition.getSide())
                .eq(Strings.isNotBlank(condition.getSyncStatus()), StrategyStopOrderContract::getSyncStatus, condition.getSyncStatus())
                .eq(Strings.isNotBlank(condition.getStatus()), StrategyStopOrderContract::getStatus, condition.getStatus())
                .orderByDesc(StrategyStopOrderContract::getCreateTime);

        page = baseMapper.selectPage(page, queryWrapper);
        List<StrategyStopOrderContract> list = page.getRecords();
        if (CollectionUtils.isEmpty(list)) {
            return page;
        }

        // 遍历
        list.forEach(i -> {
            // 翻译字典项
            i.setSide(StrategyOrderContractEnum.Side.findName(i.getSide()));
            i.setNumType(StrategyStopOrderContractEnum.NumType.findName(i.getNumType()));
            i.setSideType(StrategyStopOrderContractEnum.SideType.findName(i.getSideType()));
            i.setHasStopProfit(CommonDictEnum.YesOrNo.findName(i.getHasStopProfit()));
            i.setStopProfitType(StrategyStopOrderContractEnum.StopType.findName(i.getStopProfitType()));
            i.setStopProfitPriceType(StrategyStopOrderContractEnum.PriceType.findName(i.getStopProfitPriceType()));
            i.setHasStopLost(CommonDictEnum.YesOrNo.findName(i.getHasStopLost()));
            i.setStopLostType(StrategyStopOrderContractEnum.StopType.findName(i.getStopLostType()));
            i.setStopLostPriceType(StrategyStopOrderContractEnum.PriceType.findName(i.getStopLostPriceType()));
            i.setActualTriggerSide(StrategyStopOrderContractEnum.ActualTriggerSide.findName(i.getActualTriggerSide()));
            i.setSyncStatus(StrategyStopOrderContractEnum.SyncStatus.findName(i.getSyncStatus()));
            i.setStatus(StrategyStopOrderContractEnum.Status.findName(i.getStatus()));
        });

        return page;
    }

    /**
     * 新增
     *
     * @param dto dto
     * @since 2023/2/8 15:17
     */
    @Override
    public void add(StrategyStopOrderContractAddDTO dto) {
        strategyStopOrderContractHandlerManager.place(dto);
    }

    /**
     * 批量新增
     *
     * @param dto dto
     * @since 2023/2/12 19:16
     */
    @Override
    public void addBatch(StrategyStopOrderContractAddBatchDTO dto) {
        // 遍历转化参数，发送mq消息
        StrategyStopOrderContractAddDTO addDTO = BeanUtil.copyProperties(dto, StrategyStopOrderContractAddDTO.class);
        dto.getTaskIdList().forEach(taskId -> {
            addDTO.setTaskId(taskId);
            strategyContractUtil.sendMqStopOrderCreate(addDTO);
        });
    }

    /**
     * 批量撤销
     *
     * @param dto dto
     * @since 2023/2/10 15:34
     */
    @Override
    public void cancelBatch(StrategyStopOrderContractCancelBatchDTO dto) {
        strategyStopOrderContractHandlerManager.cancelBatch(dto);
    }

    /**
     * 撤销任务的所有止盈止损
     *
     * @param taskId 任务id
     * @since 2023/2/12 17:06
     */
    @Override
    public void cancelByTask(Long taskId) {
        StrategyStopOrderContractCancelBatchDTO dto = new StrategyStopOrderContractCancelBatchDTO();
        dto.setTaskId(taskId);
        strategyTaskContractHandler.cancelByTask(dto);
    }

    /**
     * 批量撤销任务的所有止盈止损
     *
     * @param taskIdList 任务idList
     * @since 2023/2/12 17:06
     */
    @Override
    public void cancelByTaskBatch(List<Long> taskIdList) {
        taskIdList.forEach(taskId -> {
            StrategyStopOrderContractCancelBatchDTO dto = new StrategyStopOrderContractCancelBatchDTO();
            dto.setTaskId(taskId);
            strategyContractUtil.sendMqStopOrderCancel(dto);
        });
    }

    /**
     * 一键撤销
     *
     * @param strategyId 策略id
     * @since 2023/2/12 17:17
     */
    @Override
    public void cancelAll(Long strategyId) {
        // 获取分布式锁
        String lockKey = String.format(StrategyRedisLockConstant.STRATEGY_OPERATION_ALL, strategyId);
        RLock lock = redisUtils.getLock(lockKey, null);
        if (Objects.isNull(lock)) {
            throw new DuckweedException("该策略拥有正在处理中的一键批量操作，请稍后重试");
        }

        try {
            PigxUser user = SecurityUtil.getLoginUserNotNull();
            LocalDateTime now = LocalDateTime.now();
            // 发送mq，系统日志
            SystemLog systemLog = new SystemLog();
            securityUtil.initCreateAbout(systemLog, user, now);
            systemLog.setLogId(commonUtil.getId());
            systemLog.setCreateName(user.getName());
            systemLog.setUpdateName(user.getName());
            systemLog.setOperatorType(SystemLogEnum.OperatorType.MANAGER.getValue());
            systemLog.setType(SystemLogEnum.Type.STRATEGY_STOP_ORDER_CANCEL);
            systemLog.setOldData(null);
            systemLog.setNewData("交易员发起止盈止损一键撤销，strategyId：" + strategyId);
            strategyContractUtil.sendMqSystemLog(systemLog);

            // 遍历查询运行中的任务id，发送mq
            List<Long> idList;
            int pageNum = 1;
            int pageSize = 10000;

            List<String> statusList = Arrays.asList(StrategyTaskContractEnum.Status.PAUSE.getValue(),
                    StrategyTaskContractEnum.Status.RUNNING.getValue());

            StrategyStopOrderContractCancelBatchDTO dto = new StrategyStopOrderContractCancelBatchDTO();
            dto.setLogId(systemLog.getId());

            while (!CollectionUtils.isEmpty(idList = strategyTaskContractMapper.getIdListByStrategyIdAndStatusList(strategyId,
                    statusList,
                    CommonUtil.getStartIndex(pageNum, pageSize),
                    CommonUtil.getEndIndex(pageNum, pageSize)))) {

                // 遍历发送mq消息
                idList.forEach(id -> {
                    dto.setTaskId(id);
                    strategyContractUtil.sendMqStopOrderCancel(dto);
                });

                // 增加页数
                ++pageNum;
            }

            if (pageNum == 1) {
                throw new DuckweedException("没有发现运行中的任务");
            }

        } finally {
            // 释放分布式锁
            redisUtils.releaseLock(lock);
        }
    }
}
